#ifndef SYNCQUEUE_H
#define SYNCQUEUE_H

#include <mutex>
#include <condition_variable>
#include <list>
#include <iostream>

template <class T>
class SyncQueue
{
public:
    SyncQueue(size_t maxSize = 1000) : m_maxSize(maxSize), mQuit(false) {};
    ~SyncQueue()
    {
        Clear();
        exit();
    };

    bool isFull()
    {
        return mQueue.size() >= m_maxSize;
    }
    bool isEmpty()
    {
        return mQueue.empty();
    }
    size_t Size()
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        return mQueue.size();
    }

    void Put(const T &data)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        while (isFull() && !mQuit)
        {
            con_full.wait(lock);
        }
        mQueue.push_back(data);
        con_empty.notify_one();
    };

    void Take(T &data)
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        while (isEmpty() && !mQuit)
        {
            con_empty.wait(lock);
        }
        data = mQueue.front();
        mQueue.pop_front();
        con_full.notify_one();
    };

    void Clear()
    {
        if (!mQuit)
            return;
        std::unique_lock<std::mutex> lock(m_mutex);
        mQueue.clear();
    }
    void exit()
    {
        mQuit = true;
        con_empty.notify_one();
        con_full.notify_one();
    }

private:
    std::list<T> mQueue;
    size_t m_maxSize;

    std::mutex m_mutex;
    std::condition_variable con_empty;
    std::condition_variable con_full;

    bool mQuit;
};

#endif